from django.shortcuts import render
from meiduo_mall.utils.views import LoginRequiredJSONMixin
from django.views import View
from django_redis import get_redis_connection
from django import http
from decimal import Decimal
from apps.goods.models import SKU
import json
from apps.users.models import Address
from apps.orders.models import OrderInfo, OrderGoods
from django.utils import timezone
from django.db import transaction


# Create your views here.

class OrderSettlementView(LoginRequiredJSONMixin, View):
    """结算订单"""

    def get(self, request):
        # 查收货地址
        addresses_model_list = request.user.addresses.filter(is_deleted=False)
        # 查redis数据,商品 数量 勾选状态
        redis_conn = get_redis_connection('carts')
        cart_dict = redis_conn.hgetall('carts_%s' % request.user.id)
        selecteds_list = redis_conn.smembers('selected_%s' % request.user.id)

        new_cart = {}
        for sku_id in selecteds_list:
            new_cart[int(sku_id)] = int(cart_dict[sku_id])

        addresses = []
        for address in addresses_model_list:
            addresses.append({
                "id": address.id,
                "province": address.province.name,
                "city": address.city.name,
                "district": address.district.name,
                "place": address.place,
                "mobile": address.mobile,
                "receiver": address.receiver,
            })

        skus_id = new_cart.keys()
        skus_model_list = SKU.objects.filter(id__in=skus_id)
        skus = []
        for sku in skus_model_list:
            skus.append({
                "id": sku.id,
                "name": sku.name,
                "default_image_url": sku.default_image.url,
                "count": new_cart[sku.id],
                "price": sku.price,
            })

        # 金钱类的数据精度要非常高,邮费
        freight = Decimal(10.0)

        context = {
            'addresses': addresses,
            'skus': skus,
            'freight': freight
        }

        return http.JsonResponse({'code': 0, 'errmsg': 'ok', 'context': context})


class OrderCommitView(View):
    """订单提交"""

    def post(self, request):
        """保存订单信息和订单商品信息"""
        # 获取当前要保存的订单数据
        json_dict = json.loads(request.body.decode())
        address_id = json_dict.get('address_id')
        pay_method = json_dict.get('pay_method')
        # 校验参数
        if not all([address_id, pay_method]):
            return http.JsonResponse({'code': 400, 'errmsg': '缺少必传参数'})
        # 判断address_id是否合法
        try:
            address = Address.objects.get(id=address_id)
        except Exception:
            return http.JsonResponse({'code': 400, 'errmsg': '参数address_id错误'})
        # 判断pay_method是否合法
        if pay_method not in [OrderInfo.PAY_METHODS_ENUM['CASH'], OrderInfo.PAY_METHODS_ENUM['ALIPAY']]:
            return http.JsonResponse({'code': 400, 'errmsg': '参数pay_method错误'})

        # 开启事物 # 创建事务保存点
        with transaction.atomic():
            save_id = transaction.savepoint()

            try:
                # 生成订单号
                order_id = timezone.localtime().strftime('%Y%m%d%H%M%S') + ('%09d' % request.user.id)

                # 保存订单基本信息到 OrderInfo
                order = OrderInfo.objects.create(
                    order_id=order_id,
                    user=request.user,
                    address=address,
                    total_count=0,
                    total_amount=Decimal(0.00),
                    freight=Decimal(10.00),
                    pay_method=pay_method,
                    status=OrderInfo.ORDER_STATUS_ENUM['UNSEND'] if pay_method == OrderInfo.PAY_METHODS_ENUM['CASH'] else
                    OrderInfo.ORDER_STATUS_ENUM['UNPAID']
                )

                # 从redis读取购物车中被勾选的商品信息
                redis_conn = get_redis_connection('carts')
                cart_dict = redis_conn.hgetall('carts_%s' % request.user.id)
                selecteds_list = redis_conn.smembers('selected_%s' % request.user.id)

                new_cart = {}
                for sku_id in selecteds_list:
                    new_cart[int(sku_id)] = int(cart_dict[sku_id])

                sku_ids = new_cart.keys()
                # 遍历购物车中被勾选的商品信息
                for sku_id in sku_ids:
                    # 每个商品都是在一个死循环中下单的，有资源竞争就继续，直到库存不足为止
                    while True:
                        # 查询SKU信息
                        sku = SKU.objects.get(id=sku_id)

                        # 判断SKU库存
                        buy_count = new_cart.get(sku_id)
                        sku_stock = sku.stock
                        sku_sales = sku.sales
                        if buy_count > sku_stock:
                            transaction.savepoint_rollback(save_id)
                            return http.JsonResponse({'code': 400, 'errmsg': '库存不足'})

                        # SKU减少库存 增加销量
                        # sku_stock -= buy_count
                        # sku_sales += buy_count
                        # sku.save()

                        # 计算新的库存和销量
                        new_stock = sku_stock - buy_count
                        new_sales = sku_sales + buy_count
                        # 使用乐观锁操作SKU减少库存，增加销量
                        # update()的返回值，是影响的行数。如果冲突检测发现资源竞争，那么就不执行update，返回0。反之，就执行update，返回影响的函数
                        result = SKU.objects.filter(id=sku_id,stock=sku_stock).update(stock=new_stock,sales=new_sales)
                        if result == 0:
                            # 说明检测到冲突，有资源竞争，重新下单，直到库存不足为止
                            # 每个商品都是在一个死循环中下单的，有资源竞争就继续，直到库存不足为止
                            continue

                        # 修改SPU销量
                        sku.spu.sales += buy_count
                        sku.spu.save()

                        # 保存订单商品信息 OrderGoods
                        OrderGoods.objects.create(
                            order=order,
                            sku=sku,
                            count=buy_count,
                            price=sku.price
                        )
                        # 保存商品订单中总价和总数量
                        order.total_count += buy_count
                        order.total_amount += sku.price * buy_count

                        # 如果该商品购买成功，我们就跳出死循环
                        break

                # 添加邮费和保存订单信息
                order.total_amount += order.freight
                order.save()

            except Exception:
                transaction.savepoint_rollback(save_id)
                return http.JsonResponse({'code': 400, 'errmsg': '订单提交失败'})

            # 事物提交
            transaction.savepoint_commit(save_id)

        # 清除购物车中已结算的商品
        pl = redis_conn.pipeline()
        pl.hdel('carts_%s' % request.user.id, *selecteds_list)
        pl.srem('selected_%s' % request.user.id, *selecteds_list)
        pl.execute()

        # 响应提交订单结果
        return http.JsonResponse({'code': 0, 'errmsg': 'ok', 'order_id': order_id})
